package com.zhike.service.im.manager;

import com.zhike.common.dto.*;
import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.google.gson.Gson;
import com.zhike.mobileimsdk.server.protocal.Protocal;
import com.zhike.utils.MessageHelper;
import lombok.extern.slf4j.Slf4j;

import com.zhike.utils.push.ios.APNSPushHelper;

/**
 * 离线消息处理逻辑管理器。
 * <p>
 * 所有有关离线的处理逻辑都在本类中集中完成，方便统一管理。
 * 
 * <p>
 * <b>离线消息处理逻辑在什么地方实现合适？</b><br>
 * <br>
 * 详见“RainbowChat_pro_v4关键设计思路备忘.txt”。
 * 
 * @author Tony
 * @since 4.5
 */
@Slf4j
@Component
public class OfflineMessageManager {
  @Resource
  private APNSPushHelper apnsPushHelper;


  @Autowired
  private MessageRecordManager messageRecordManager;

  // 离线消息缓存时间1天(毫秒)
  public static final Long sec = 1000 * 24 * 60 * 60L;

  /**
   * 处理离线聊天消息（专用于普通聊天消息(含一对一、群聊））
   * 
   * @param p             原始Protocal协议包对象
   * @param onlyDoAppPush true表示不做其它任何离线处理逻辑只做App的Push通知，否则正常走离线处理完整逻辑（包括App的
   *                      Push通知），正常情况本参数设为false即可。为true时，用于桥接通信成功，但手机端确实不在本App
   *                      Server上，那么此时
   *                      做一个only
   *                      push（不需要离线存储逻辑，否则web那端再搞离线存储那消息就重复了），是合理的，这就是App这端的特殊情况！
   * @return
   */
  public boolean processOfflineMessage(Protocal p, boolean onlyDoAppPush) {
    // 接收者uid
    String to_userId = null;
    // 发送者uid
    String from_userId = null;
    // 消息或指令内容（一般而言，这会是个JSON字符串，因为是个复合对象）
    String dataContent = p.getDataContent();
    // 消息或指令指纹码（即唯一ID）
    String fingerPrint = p.getFp();

    // 用于离线存储时的聊天模式类型（见 MsgBodyRoot中的常量定义 ）
    int chatType = -1;
    // 用于离线存储时的消息内容类型（见 MsgBodyRoot中的常量定义 ）
    int msgType = -1;

    // 本字段只有在聊天模式类型chatType为“2 群聊”时有意义且不应为空，表示本条群聊消息发于在哪个群里
    String group_id = null;
    // 本字段只有在聊天模式类型chatType为“2 群聊”时有意义，表示群聊消息扩散写前原始消息的指纹码（目前主要用于消息“撤回”功能时）
    String parent_fp = null;

    String msg = null;

    // 本字段只有在聊天模式类型chatType为“2 群聊”时有意义，用于群聊消息时存放被"@"者列表（是String[] 数组的JSON字串形式）
    String be_at = null;

    // 消息引用数据
    QuoteMeta quoteMeta = null;

    log.debug("协议------------ {}", p.getTypeu());
    switch (p.getTypeu()) {
      // 一对一好友聊天消息用于离线处理的协议解析逻辑（C2C方向）
      case UserProtocalsType.MT03_OF_CHATTING_MESSAGE: {
        MsgBodyRoot msgBody = MsgBodyRoot.parseFromSender(dataContent);

        quoteMeta = msgBody;

        to_userId = p.getTo();
        from_userId = p.getFrom();
        chatType = msgBody.getCy();// MsgBodyRoot.CHAT_TYPE_FREIDN$CHAT;

        group_id = null;
        parent_fp = null;
        msg = msgBody.getM();
        msgType = msgBody.getTy();
        messageRecordManager.colectChattingMsgAsync(to_userId, dataContent, msgType, fingerPrint);
        log.debug("离线消息存储: toUid:{} {}", to_userId, dataContent);
        break;
      }
      // 一对一陌生人聊天消息用于离线处理的协议解析逻辑（S2C方向）
      case UserProtocalsType.MT43_OF_TEMP$CHAT$MSG_SERVER$TO$B: {
        // 解析临时聊天消息体内容(dataContent直接传的是Protocal的content字段哦，也就是MsgBody4Guest的JSON串！)
        MsgBody4Guest msgBody = MessageHelper.pareseTempChatMsg_A$TO$SERVER_Message(dataContent);

        quoteMeta = msgBody;

        to_userId = msgBody.getT();
        from_userId = msgBody.getF();
        chatType = msgBody.getCy();

        group_id = null;
        parent_fp = null;
        msg = msgBody.getM();
        msgType = msgBody.getTy();

        break;
      }
      // 一对对群聊天消息用于离线处理的协议解析逻辑（S2C方向）
      case UserProtocalsType.MT45_OF_GROUP$CHAT$MSG_SERVER$TO$B: {
        // 解析群组聊天消息体内容(dataContent直接传的是Protocal的content字段哦，也就是MsgBody4Guest的JSON串！)
        MsgBody4Group msgBody = MessageHelper.pareseGroupChatMsg_A$TO$SERVER_Message(dataContent);

        quoteMeta = msgBody;

        to_userId = p.getTo();// 本条扩散写消息的接收人
        from_userId = msgBody.getF();
        chatType = msgBody.getCy();

        group_id = msgBody.getT(); // 群聊时，协议体msgBody中的t字段存放的就是群id（这是在im协议里约定好的，详见IM协议手册）
        parent_fp = msgBody.getParentFp();
        msg = msgBody.getM();
        msgType = msgBody.getTy();

        String[] be_ats = msgBody.getAt();
        be_at = (be_ats != null ? new Gson().toJson(be_ats) : null);

        break;
      }

      /*******************************
       * 以下指令类型属于群聊系统通知 START
       *************************************/
      case UserProtocalsType.MT46_OF_GROUP$SYSCMD_MYSELF$BE$INVITE_FROM$SERVER: {
        CMDBody4MyselfBeInvitedGroupResponse cmdBody = new Gson().fromJson(
            p.getDataContent(), CMDBody4MyselfBeInvitedGroupResponse.class);

        to_userId = p.getTo();// 本条扩散写消息的接收人
        from_userId = "0";// 消息的发起人（这是“服务端”发出的，所以是“0”）
        chatType = MsgBodyRoot.CHAT_TYPE_GROUP$CHAT;

        group_id = cmdBody.getGroupId();
        parent_fp = null;
        msg = "\"" + cmdBody.getInitveBeNickName() + "\"邀请你加入了群聊";
        msgType = MsgBodyRoot.TYPE_SYSTEAM$INFO;

        break;
      }
      case UserProtocalsType.MT47_OF_GROUP$SYSCMD_COMMON$INFO_FROM$SERVER:
      case UserProtocalsType.MT48_OF_GROUP$SYSCMD_DISMISSED_FROM$SERVER:
      case UserProtocalsType.MT49_OF_GROUP$SYSCMD_YOU$BE$KICKOUT_FROM$SERVER:
      case UserProtocalsType.MT50_OF_GROUP$SYSCMD_SOMEONEB$REMOVED_FROM$SERVER: {
        MsgBody4Group body = new Gson().fromJson(p.getDataContent(), MsgBody4Group.class);

        quoteMeta = body;

        to_userId = p.getTo();// 本条扩散写消息的接收人
        from_userId = "0";// 消息的发起人（这是“服务端”发出的，所以是“0”）
        chatType = MsgBodyRoot.CHAT_TYPE_GROUP$CHAT;

        group_id = body.getT();
        parent_fp = null;
        msg = body.getM();
        msgType = MsgBodyRoot.TYPE_SYSTEAM$INFO;

        break;
      }
      case UserProtocalsType.MT51_OF_GROUP$SYSCMD_GROUP$NAME$CHANGED_FROM$SERVER: {
        CMDBody4GroupNameChangedNotification cmdBody = new Gson().fromJson(p.getDataContent(),
            CMDBody4GroupNameChangedNotification.class);

        to_userId = p.getTo();// 本条扩散写消息的接收人
        from_userId = "0";// 消息的发起人（这是“服务端”发出的，所以是“0”）
        chatType = MsgBodyRoot.CHAT_TYPE_GROUP$CHAT;

        group_id = cmdBody.getGid();
        parent_fp = null;
        msg = cmdBody.getNotificationContent();
        msgType = MsgBodyRoot.TYPE_SYSTEAM$INFO;

        break;
      }
      /*******************************
       * 以下指令类型属于群聊系统通知 END
       *************************************/
    }

    if (onlyDoAppPush) {
      try {
        // MsgBodyRoot tm = MsgBodyRoot.parseFromSender(dataContent);

        // ## 尝试向ios设备进行APNs离线消息推送（推送是基于netty+HTTP/2实现，因而是异步的不会阻塞余下代码执行）
        apnsPushHelper.tryPushChattingMessage(chatType, msgType, msg, from_userId, to_userId, group_id);

        return true;
      } catch (Exception e) {
        log.warn(e.getMessage(), e);
        return false;
      }
    } else {
      log.debug("[ZKIMChat]【离线存储】[chatType=" + chatType + ",msgType=" + msgType + ",gid=" + group_id
          + "]正在处理：" + from_userId + "发给" + to_userId + "的离线消息：str=" + dataContent + ",fingerPring=" + fingerPrint
          + ",parent_fp=" + parent_fp + "......");
      try {

        if (msg != null && from_userId != null && to_userId != null) {
          // HistoryMessage message =
          // historyMessageRepository.findHistoryMessageByMessageId(fingerPrint);
          // if (message != null) {
          // log.debug("[ZKIMChat]【离线存储】[chatType=" + chatType + ",msgType=" + msgType +
          // ",gid=" + group_id
          // + "]" + from_userId + "发给" + to_userId + "的离线消息：str=" + dataContent
          // + ",fingerPring=" + fingerPrint + ",parent_fp=" + parent_fp + "
          // 已经被放入DB中存储了，本条可能是客户"
          // + "端QoS机制重复发过来的，将被忽略(无需存储).");
          // } else {
          // // ## 尝试向ios设备进行APNs离线消息推送（推送是基于netty+HTTP/2实现，因而是异步的不会阻塞余下代码执行）
          // apnsPushHelper.tryPushChattingMessage(chatType, msgType, msg, from_userId,
          // to_userId, group_id);

          // Device d_from = deviceInfoCacheProvider.get(from_userId);
          // if (d_from == null) {
          // log.error("未找到发送者设备{}缓存数据", from_userId);
          // return false;
          // }
          // from_userId = d_from.getFakeUid();

          // Device d_to = deviceInfoCacheProvider.get(to_userId);
          // if (d_to == null) {
          // log.error("未找到接收者设备{}缓存数据", from_userId);
          // return false;
          // }
          // to_userId = d_to.getFakeUid();

          // // 更新最后一条信息
          // String sessionId = "";
          // ChatSessionDTO cs1 = new ChatSessionDTO();
          // cs1.setFromUid(d_from.getFakeUid());
          // cs1.setToUid(d_to.getFakeUid());
          // cs1.setChatType(String.valueOf(chatType));
          // cs1.setMsgType(String.valueOf(msgType));
          // cs1.setLastMsg(msg);
          // cs1.setLastTime(new Date());
          // cs1.setSessionId(IdUtil.getSnowflakeNextIdStr());
          // sessionId = chatSessionService.updateUserChatSession(cs1);
          // ChatSessionDTO cs2 = ObjectUtil.clone(cs1);
          // cs2.setFromUid(d_to.getFakeUid());
          // cs2.setToUid(d_from.getFakeUid());
          // sessionId = chatSessionService.updateUserChatSession(cs2);

          // // 历史消息
          // messageRecordManager.colectMsgAsync(sessionId, from_userId, to_userId,
          // chatType, String.valueOf(msgType),
          // msg, Protocal.genFingerPrint());

        }
        // } else {
        // log.error("[ZKIMChat]【离线存储】[chatType=" + chatType + ",msgType=" + msgType +
        // ",gid=" + group_id
        // + "]离线消息数据有误，该条离线消息无法存储到DB中：("
        // + from_userId + "发给" + to_userId + "的离线消息：str=" + dataContent +
        // ",fingerPring=" + fingerPrint
        // + ",parent_fp=" + parent_fp + ")");
        // }
        return true;
      } catch (Exception e) {
        log.debug("[ZKIMChat]【离线存储】[chatType=" + chatType + ",msgType=" + msgType + ",gid=" + group_id
            + "]处理离线存储消息时出错了 -> " + from_userId + "发给" + to_userId + "的离线消息：str=" + dataContent + ",fingerPring="
            + fingerPrint + ",parent_fp=" + parent_fp, e);
      }

      return false;
    }
  }

}
